/***************************************************************************
 *   Copyright (C) 2010 by Oleg Goncharov  *
 *   $EMAIL$                           *                          
 *                                                                         *
 *   This file is part of ChessVision.                                     *
 *                                                                         *
 *   ChessVision is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <cmath>

#include "chesstypes.h"

namespace Chess {

void SMove::ToString(std::string& str) const {
	str = "a1a1";
	str[0] += from % 8;
	str[1] += from / 8;
	str[2] += to % 8;
	str[3] += to / 8;
}

void SMove::FromString(const std::string& str) {
	from = str[0]-'a' + 8*(str[1]-'1');
	to = str[2]-'a' + 8*(str[3]-'1');
}	

CPosition::CPosition(bool place_figures) { 
	if (place_figures) InitialPosition(); 
	else Clear();
}
		
void CPosition::Clear() {
	for(int i = 0; i < 64; i++) position[i] = empty;
}

void CPosition::InitialPosition() {
	position[0] = wRook;
	position[1] = wKnight;
	position[2] = wBishop;
	position[3] = wQueen;
	position[4] = wKing;
	position[5] = wBishop;
	position[6] = wKnight;
	position[7] = wRook;
	for(int i = 8; i < 16; i++) position[i] = wPawn;
	for(int i = 48; i < 56; i++) position[i] = bPawn;
	for(int i = 56; i < 64; i++) position[i] = (Figure) -position[i - 56];
	for(int i = 16; i < 48; i++) position[i] = empty;
}

void CPosition::RotateHalfPI() {
	int p = 0;
	Figure temp[64];
	//TODO there isn't the best solutuion
	for(int i = 0; i < 64; i++) temp[i] = position[i];
	for(int i = 0; i < 8; i++)
		for(int j = 7; j >= 0; j--) 
			position[i + 8*j] = temp[p++];
}

void CPosition::RotatePI() {
	int i, j;
	Figure tmp;
	for(i = 0, j = 63; i < 64; i++, j--) {
		tmp = position[i]; 
		position[i] = position[j]; 
		position[j] = tmp;
	}
}

int CPosition::Difference(const CPosition& prev, FigureList& disappear, FigureList& appear, FigureList& change) const {
	disappear.clear(); appear.clear(); change.clear();
		
	for(Square i = 0; i < 64; i++) {
		if (IsEmpty(i) && !prev.IsEmpty(i)) disappear.push_back(i); 
		else if (!IsEmpty(i) && prev.IsEmpty(i)) appear.push_back(i);
		else if ( position[i]*prev.position[i] < 0 ) change.push_back(i);
	}
	return appear.size() + disappear.size() + change.size();
}

int CPosition::Difference(const CPosition& prev) const {
	int diff = 0;
	for(Square i = 0; i < 64; i++) {
		if (IsEmpty(i) && !prev.IsEmpty(i)) diff++; 
		else if (!IsEmpty(i) && prev.IsEmpty(i)) diff++;
		else if ( position[i]*prev.position[i] < 0 ) diff++;
	}
	return diff;
}

bool CPosition::CheckCastle(const SMove& mv, SMove& rook_move) const { 
	if (mv.from == 4 && position[4] == wKing || mv.from == 60 && position[60] == bKing) {
		switch (mv.to - mv.from) {
		case -2: 
			rook_move.from = mv.from - 4; 
			rook_move.to = mv.from - 1;
			return true;
		case 2: 
			rook_move.from = mv.from + 3; 
			rook_move.to = mv.from  + 1;
			return true;
		}
	}
	return false;
}

void CPosition::Move(const SMove& mv) { 
	SMove rook;
	if (CheckCastle(mv, rook)) {
		position[rook.to] = position[rook.from]; 
		position[rook.from] = empty;
	}
	position[mv.to] = position[mv.from]; 
	position[mv.from] = empty;
}

void CPosition::DrawPosition(IplImage * img, const CBoardCells& cells) const {
	cells.DrawCells(img);
	for(int i = 0; i < 64; i++) {
		if (IsBlack(i)) cvCircle(img, cells.Cell(i), 4, cvScalar(0,0,255), -1);
		if (IsWhite(i)) cvCircle(img, cells.Cell(i), 4, cvScalar(0,255,0), -1);
	}
}

void CPosition::DrawPosition(cv::Mat& img, const CBoardCells& cells) const {
	cells.DrawCells(img);
	for(int i = 0; i < 64; i++) {
		if (IsBlack(i)) cv::circle(img, cells.Cell(i), 4, cv::Scalar(0,0,255), -1);
		if (IsWhite(i)) cv::circle(img, cells.Cell(i), 4, cv::Scalar(0,255,0), -1);
	}
}

///////////////////// class CBoardCells ////////////////

//TODO Think about programming style.
void CBoardCells::Init(const SBoardPosition& bp) {
	h = bp.h;
	halfh = h / 2;
	float dx = h * cos(bp.teta);
	float dy = h * sin(bp.teta);
	cv::Point2f s(-(dx - dy) * 3.5f + bp.x, -(dx + dy) * 3.5f + bp.y);	
	for(int i = 0; i < 8; i++)
		for(int j = 0; j < 8; j++) {
			centers[i + 8*j].x = s.x + i*dx - j*dy;
			centers[i + 8*j].y = s.y + i*dy + j*dx;
		}
	s.x = -(dx - dy) * 4.0f + bp.x;
	s.y = -(dx + dy) * 4.0f + bp.y;
	for(int i = 0; i < 9; i++)
		for(int j = 0; j < 9; j++) {
			corners[i + 9*j].x = s.x + i*dx - j*dy;
			corners[i + 9*j].y = s.y + i*dy + j*dx;
		}
}
	
void CBoardCells::Init(const Line2f * line1, const Line2f * line2) {
	for(int i = 0; i < 9; i++)
		for(int j = 0; j < 9; j++)
			line1[i].Intersect(line2[j], corners[i + 9*j]);
			
	h = 0.0f;
	int b9, b8;
	for(int i = 0; i < 8; i++) 
		for(int j = 0; j < 8; j++) {
			b8 = i + 8*j;
			b9 = i + 9*j;
			centers[b8] = (corners[b9] + corners[b9 + 1] + corners[b9 + 9] + corners[b9 + 10]);
			centers[b8].x /= 4; 
			centers[b8].y /= 4;
			h += cv::norm(corners[b9 + 1] - corners[b9]) + cv::norm(corners[b9 + 10] - corners[b9 + 1]);
		}
	h /= 128;
	halfh = h / 2;
}

void CBoardCells::Rotate(float angle) {
	cv::Point2f o = corners[4 + 9*4];
	float sinus = sinf(angle);
	float cosin = cosf(angle);
	cv::Point2f p;
	cv::Point2f new_p;
	
	for(int i = 0; i < 64; i++) {
		p = centers[i] - o;
		centers[i].x = cosin*p.x - sinus*p.y + o.x;
		centers[i].y = sinus*p.x + cosin*p.y + o.y;
	}
	for(int i = 0; i < 81; i++) {
		p = corners[i] - o;
		corners[i].x = cosin*p.x - sinus*p.y + o.x;
		corners[i].y = sinus*p.x + cosin*p.y + o.y;
	}
}

void CBoardCells::DrawCells(cv::Mat& img) const {
	cv::circle(img, Corner(0, 0), 3, cvScalar(255,0,0), -1);
	cv::circle(img, Corner(8, 0), 3, cvScalar(255,0,0), -1);
	
	for(int i = 0; i < 9; i++) {
		cv::line(img, Corner(0, i), Corner(8, i), cv::Scalar(255,0,0), 1);
		cv::line(img, Corner(i, 0), Corner(i, 8), cv::Scalar(255,0,0), 1);
	}
	for(int i = 0; i < 64; i++) {
		cv::circle(img, Cell(i), 3, cv::Scalar(4*i,0,0), -1);
	}
}

void CBoardCells::DrawCells(IplImage * _img) const {
	cv::Mat img = _img;
	
	cv::circle(img, Corner(0, 0), 3, cvScalar(255,0,0), -1);
	cv::circle(img, Corner(8, 0), 3, cvScalar(255,0,0), -1);
	
	for(int i = 0; i < 9; i++) {
		cv::line(img, Corner(0, i), Corner(8, i), cvScalar(255,0,0), 1);
		cv::line(img, Corner(i, 0), Corner(i, 8), cvScalar(255,0,0), 1);
	}
	for(int i = 0; i < 64; i++) {
		cv::circle(img, Cell(i), 3, cvScalar(4*i,0,0), -1);
	}
}

/////////////////////////////

void SBoardCells::Init(float x, float y, float teta, float _h) {
	h = (int) _h;
	dx = _h * cos(teta);
	dy = _h * sin(teta);
	sx = -(dx - dy) * 3.5f + x;
	sy = -(dx + dy) * 3.5f + y;	
	cx = -(dx - dy) * 4.0f + x;
	cy = -(dx + dy) * 4.0f + y;	
}

void SBoardCells::Rotate(float angle) {
	float ox = cx + 4*(dx - dy); 
	float oy = cy + 4*(dy + dx);
	float sinus = sinf(angle);
	float cosin = cosf(angle);
	float new_dx = cosin*dx - sinus*dy;
	float new_dy = sinus*dx + cosin*dy;
	dx = new_dx; dy = new_dy;
	cx = ox - 4*(dx - dy); cy = oy - 4*(dy + dx);
	sx = ox - 3.5f*(dx - dy); sy = oy - 3.5f*(dy + dx);
}

void SBoardCells::DrawCells(IplImage * dst) const {
	cv::Point2f lp, rp;
	
	lp = Corner();
	cvCircle(dst, lp, 3, cvScalar(255,0,0), -1);
	cvCircle(dst, Corner(8,0), 3, cvScalar(255,0,0), -1);
	
	rp = Corner(8, 0);
	for(int i = 0; i < 9; i++) {
		cvLine(dst, lp, rp, cvScalar(255,0,0), 1);
		StepY(lp); StepY(rp);
	}
	
	lp = Corner();
	rp = Corner(0, 8);
	for(int i = 0; i < 9; i++) {
		cvLine(dst, lp, rp, cvScalar(255,0,0), 1);
		StepX(lp); StepX(rp);
	}
	
/*	CvScalar scal;
	
	SBoardCells cells(200, 200, 3.14, 30);
	cv::Point2f sp = cells.Start(), p;
	int twor = 20;
	double integ = 0;
	
	for(int i = 0; i < 8; i++) {
		p = sp;
		for(int j = 0; j < 8; j++) {
			cvRectangle(dst, cvPoint(p.x, p.y), cvPoint(p.x +twor , p.y + twor), cvScalarAll(127), -1);
			cells.StepY(p);
		}
		cells.StepX(sp);
	}*/
}

}

